package ipcards.net;
import ipcards.events.*;
import ipcards.*;

import java.awt.Point;
import java.util.LinkedList;
import java.util.Vector;



/**
 * Models player actions on the server side for a single {@link Client}.
 * 
 * @author Ricky Vincent
 */
public class ServerModel implements PlayerActionListener, PlayerActionPerformer {

	private static final char SEE = 0;
	private static final char CONTROL = 1;
	private static final char PERMISSION = 2;

	/**
	 * The {@link Table} this client is at.
	 */
	private Table table;
	
	/**
	 * Cards held by this client
	 */
	private CardSet heldCards = new CardSet();
	
	/**
	* cards locally flipped by this client
	*/
	private CardSet locallyFlippedCards = new CardSet();
	
	/**
	 * Reference to the {@link Client} object
	 */
	private Client client;
	
	
	/**
	 * If a game needs to add more decks due to too many people for one deck, this will indicate how many extra decks are needed
	 */
	public int addDecks = 0;
	
	
	/**
	 * Constructs a ServerModel object.
	 * 
	 * @param table The {@link Table} this client is at.
	 */
	public ServerModel (Table table, Client client) {
		this.table = table;
		this.table.sModel = this;
		this.client = client;
	}
	
	
	/**
	 * Flips all of the held cards.
	 */
	public CardSet flip() {
		
		CardSet newSet = new CardSet();
		
		if(heldCards.isEmpty())
			return newSet;
		
		for (Card card: heldCards) {
			if(card.permissionQuery(client.thisplayer, SEE)) {
				card.toggleFaceUp();
				newSet.add(card);
			}
		}
		return newSet;
	}
	
	public CardSet setFaceUp() {
		
		CardSet newSet = new CardSet();
		
		if(heldCards.isEmpty())
			return newSet;
		
		for (Card card: heldCards) {
			if(card.permissionQuery(client.thisplayer, CONTROL)) {
				card.setFaceUp();
				newSet.add(card);
			}
		}
		return newSet;
	}
	
	public CardSet setFaceDown() {
		
		CardSet newSet = new CardSet();
		
		if(heldCards.isEmpty())
			return newSet;
		
		for (Card card: heldCards) {
			if(card.permissionQuery(client.thisplayer, CONTROL)) {
				card.setFaceDown();
				newSet.add(card);
			}
		}
		return newSet;
	}
	
	/**
	 * Flips the cards locally on the client 
	 * @return
	 */
	public CardSet localFlip() {
		
		CardSet newSet = new CardSet();
		
		if(heldCards.isEmpty())
			return newSet;
		
		for (Card card: heldCards) {
			if(card.permissionQuery(client.thisplayer, SEE)) {
				if(locallyFlippedCards.contains(card)) {
					//locallyFlippedCards.remove(card);
				}
				else {
					locallyFlippedCards.add(card);
				}
				newSet.add(card);
			}
		}
		return newSet;
	}
	
	public void resetLocalFlip() {
		locallyFlippedCards = new CardSet();
	}


	/**
	 * Returns the {@link Table}
	 */
	public Table getTable() {
		return table;
	}


	/**
	 * Moves the held cards by a <code>Point</code>
	 */
	public CardSet moveBy(Point point) {
		return moveBy(point.x, point.y);
	}
	
	
	/**
	 * Moves the held cards by x, y
	 */
	public CardSet moveBy(int x, int y) {
		
		CardSet newSet = new CardSet();
		
		if(heldCards.isEmpty())
			return newSet;
		
		for (Card card: heldCards) {
			if(card.permissionQuery(client.thisplayer, CONTROL)) {
				card.moveBy(x, y);
				newSet.add(card);
			}
		}
		return newSet;
	}
	
	
	/**
	 * Syncs all of the clients to the server
	 */
	public void sync() {
		String args = locationsString();
		if (!args.isEmpty())
			performPlayerAction(new PlayerAction(this, PlayerAction.SYNC_LOCATIONS, locationsString()));
	}
	
	public String locationsString() {
		String args = "";
		
		for(Card c: table.cards.ZValueOrder) {
			args += c.getID() + PlayerAction.DEL + c.getLocation().x + PlayerAction.DEL + c.getLocation().y + PlayerAction.DEL + (c.isFaceUp() ? "1" : "0") + PlayerAction.DEL;
		}
		return args.isEmpty() ? "" : args.substring(0, args.length() - 1);
	}


	/**
	 * Called when a player performs an action
	 */
	public void playerActionPerformed(PlayerAction pa) {
		PlayerAction action = null;
		CardSet returnedCards = new CardSet();
		
		switch(pa.getAction()) {
		
		case PlayerAction.HOLD:
			Vector<Card> toHold = table.getCardSetFromIDsOrdered(pa.getArgs());
			heldCards.clear();
			
			for(Card card : toHold) {
				if(card.permissionQuery(client.thisplayer, CONTROL)) {
					heldCards.add(card);
					
					//set zordering
					LinkedList<Card> zorder = table.cards.ZValueOrder;
					zorder.remove(card);
					zorder.add(card);
				}
			}
		
		break;
		
		case PlayerAction.FLIP:
			
			returnedCards = flip();
			
			if(returnedCards.isEmpty())
				return;
			
			action = new PlayerAction(this, PlayerAction.FLIP, returnedCards.toString());
			performPlayerAction(action);
		
		break;
		
		case PlayerAction.LOCAL_FLIP:
			
			returnedCards = localFlip();
			
			if(returnedCards.isEmpty())
				return;
			
			action = new PlayerAction(this, PlayerAction.LOCAL_FLIP, returnedCards.toString());
			performPlayerAction(action);
		
		break;
		
		case PlayerAction.MOVE:
			String[] xy = pa.getArgs().split(PlayerAction.DEL + "");
			
			returnedCards = moveBy(Integer.parseInt(xy[0]), Integer.parseInt(xy[1]));
		
			if(returnedCards.isEmpty())
				return;
	
			action = new PlayerAction(this, PlayerAction.MOVE, xy[0] + PlayerAction.DEL + xy[1] + PlayerAction.DEL + returnedCards.toString());
			performPlayerAction(action);
			
		break;
		
		case PlayerAction.SHUFFLE:
			if(!(table.game.legalityCheck("shuffle"))){
				break;
			}
			table.cards.shuffle();
			performPlayerAction(new PlayerAction(this, PlayerAction.SYNC_LOCATIONS, locationsString()));
		break;
		
		case PlayerAction.SYNC_LOCATIONS:
			String args = locationsString();
			if (!args.isEmpty())
				performPlayerAction(new PlayerAction(this, PlayerAction.SYNC_LOCATIONS, locationsString()));
		break;
		
		case PlayerAction.END_TURN:
			Client source = (Client)(pa.getSource());
			source.endTurn();
		break;
		
		case PlayerAction.NEW_GAME:
			if(table.game != null){
				if(!(table.game.legalityCheck("newgame"))){
					break;
				}
			}		
			Client source2 = (Client)(pa.getSource());
			if(pa.getArgs().charAt(0) == PlayerAction.SHOW_DIALOG){

				table.getRoom().controller.getCardServer().newgame(source2,10);
				table.getRoom().controller.getCardServer().gamename = pa.getArgs().substring(3);
				performPlayerAction(new PlayerAction(this, PlayerAction.NEW_GAME, pa.getArgs()));
			}
			else if(pa.getArgs().charAt(0) == PlayerAction.NEW_GAME){
				int response = Integer.parseInt(pa.getArgs().substring(1));
			
				table.getRoom().controller.getCardServer().newgame(source2,response);
			}
			if(table.getRoom().controller.getCardServer().newGameAllRespondedQuery() && table.getRoom().controller.getCardServer().newGameApproved()){			
											
				table.newGame(table.getRoom().controller.getCardServer().gamename, ((Client)(pa.getSource())));
				//System.out.println("at server?");						//IS ONLY ON SERVER...CALLED AT SERVER END REGARDLESS OF WHICH CLIENT!!!!!
addDecks = table.addDecksNumber;				
System.out.println("pion"+addDecks+"iswin");				
				performPlayerAction(new PlayerAction(this, PlayerAction.NEW_GAME, addDecks+"::"+table.getRoom().controller.getCardServer().gamename));
				CardSet toFlip = new CardSet();
				for(Card c : table.cards) {
					if(c.isFaceUp()) {
						toFlip.add(c);
					}
				}
				if(!toFlip.isEmpty()) {
					performPlayerAction(new PlayerAction(this, PlayerAction.SET_FACE_UP, toFlip.toString()));
				}
				CardSet cards = table.cards;
				performPlayerAction(new PlayerAction(this, PlayerAction.RESET_LOCAL_FLIP_TOALL, cards.toString()));
				source2.endTurn();
			}
			
			if(!(table.getRoom().controller.getCardServer().newGameApproved())){
				performPlayerAction(new PlayerAction(this, PlayerAction.NEW_GAME,PlayerAction.NEW_GAME+"no"));		
			}		
		break;
		
		case PlayerAction.ADD_DECK:
			table.addDeck(new Deck(pa.getArgs()));
			performPlayerAction(new PlayerAction(this, PlayerAction.ADD_DECK, pa.getArgs()));
		break;
		
		case PlayerAction.SET_FACE_UP:
			
			returnedCards = setFaceUp();
			
			if(returnedCards.isEmpty())
				return;
			
			action = new PlayerAction(this, PlayerAction.SET_FACE_UP, returnedCards.toString());
			performPlayerAction(action);
		break;
		
		case PlayerAction.SET_FACE_DOWN:
			
			returnedCards = setFaceDown();
			
			if(returnedCards.isEmpty())
				return;
			
			action = new PlayerAction(this, PlayerAction.SET_FACE_DOWN, returnedCards.toString());
			performPlayerAction(action);
		break;
		
		case PlayerAction.RESET_LOCAL_FLIP:
			locallyFlippedCards = new CardSet();
			performPlayerAction(new PlayerAction(this, PlayerAction.RESET_LOCAL_FLIP, table.cards.toString()));
		break;
		
		case PlayerAction.CUSTOM:
			table.game.playerActionPerformed(pa);
		break;
			
		case PlayerAction.DISCONNECT:
			 ((Client)(pa.getSource())).disconnect();
			 System.out.println("Client Disconnected");
		break;
		
		}
		
	}


	/**
	 * Called to perform an action
	 */
	public void performPlayerAction(PlayerAction pa) {
		
		switch(pa.getAction()) {
		
		case(PlayerAction.FLIP):
			client.sendCommandToAll(pa.getAction(), pa.getArgs());
		break;
		
		case(PlayerAction.LOCAL_FLIP):
			client.sendCommand(pa.getAction(), pa.getArgs());
		break;
		
		case(PlayerAction.MOVE):
			client.sendCommandToAllX(pa.getAction(), pa.getArgs());
		break;
				
		case(PlayerAction.NEW_GAME):
			if(pa.getArgs().charAt(0) == PlayerAction.SHOW_DIALOG){
				client.sendCommandToAllX(pa.getAction(), pa.getArgs());
			}
			else{
				client.sendCommandToAll(pa.getAction(), pa.getArgs());
			}
		break;
		
		case(PlayerAction.ADD_DECK):
			client.sendCommandToAll(pa.getAction(), pa.getArgs());
		break;
		
		case PlayerAction.SYNC_LOCATIONS:
			client.sendCommandToAll(pa.getAction(), pa.getArgs());
		break;
		
		case PlayerAction.SET_FACE_UP:
			client.sendCommandToAll(pa.getAction(), pa.getArgs());
		break;
		
		case PlayerAction.SET_FACE_DOWN:
			client.sendCommandToAll(pa.getAction(), pa.getArgs());
		break;
		
		case PlayerAction.RESET_LOCAL_FLIP:
			client.sendCommandToAll(pa.getAction(), pa.getArgs());
			//client.sendCommand(pa.getAction(), pa.getArgs());
		break;
		
		case PlayerAction.RESET_LOCAL_FLIP_TOALL:
System.out.println("Sending to all");		
			client.sendCommandToAll(PlayerAction.RESET_LOCAL_FLIP, pa.getArgs());
		break;
		
		case(PlayerAction.CUSTOM):
			client.sendCommand(pa.getAction(), pa.getArgs());
		break;
		
		case(PlayerAction.SHOW_DIALOG):
			client.sendCommand(pa.getAction(), pa.getArgs());
		}		
	}
}
